package com.atguigu.spring.core;

import com.atguigu.spring.core.annotation.Bean;
import com.atguigu.spring.core.annotation.Di;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * 基于注解的应用上下文实现类，实现包扫描、Bean实例化和依赖注入功能
 * 通过@Bean注解标识需要实例化的类，通过@Di注解实现属性依赖注入
 */
public class AnnotationApplicationContext implements ApplicationContext {

    private static String rootPath;
    // 存储Bean实例的容器，Key为接口类型或实现类类型，Value为Bean实例
    private final HashMap<Class<?>, Object> beanFactory = new HashMap<>();

    /**
     * 构造函数，初始化应用上下文并进行依赖注入
     *
     * @param basePackage 需要扫描的基础包路径
     * @throws RuntimeException 当资源加载失败时抛出运行时异常
     */
    public AnnotationApplicationContext(String basePackage) {
        try {
            // 将包路径转换为文件系统路径格式
            String packageDirName = basePackage.replaceAll("\\.", "\\\\");
            // 获取类路径下所有匹配的资源
            Enumeration<URL> dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
            while (dirs.hasMoreElements()) {
                URL url = dirs.nextElement();
                // 解码文件路径并计算根路径
                String filePath = URLDecoder.decode(url.getFile(), StandardCharsets.UTF_8);
                rootPath = filePath.substring(0, filePath.length() - packageDirName.length());
                // 加载指定路径下的Bean
                loadBean(new File(filePath));
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        // 对所有已加载的Bean进行依赖注入
        loadDi();
    }

    /**
     * 根据类型从容器中获取Bean实例
     *
     * @param clazz 要获取的Bean的类类型
     * @return 对应类型的Bean实例，如果不存在则返回null
     */
    @Override
    public Object getBean(Class<?> clazz) {
        return beanFactory.get(clazz);
    }

    /**
     * 递归加载指定目录下的所有类文件，实例化带有@Bean注解的类
     *
     * @param fileParent 要扫描的目录文件对象
     */
    private void loadBean(File fileParent) throws Exception {
        if (fileParent.isDirectory()) {
            File[] childrenFiles = fileParent.listFiles();
            if (childrenFiles == null || childrenFiles.length == 0) {
                return;
            }
            // 遍历目录中的所有文件
            for (File child : childrenFiles) {
                if (child.isDirectory()) {
                    // 递归处理子目录
                    loadBean(child);
                } else {
                    // 处理类文件：转换路径为全类名并实例化
                    String pathWithClass = child.getAbsolutePath().substring(rootPath.length() - 1);
                    if (pathWithClass.contains(".class")) {
                        // 转换为标准类名格式（com.example.ClassName）
                        String fullName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");
                        try {
                            Class<?> aClass = Class.forName(fullName);
                            // 仅处理非接口且带有@Bean注解的类
                            if (!aClass.isInterface()) {
                                Bean annotation = aClass.getAnnotation(Bean.class);
                                if (annotation != null) {
                                    Object instance = aClass.getDeclaredConstructor().newInstance();
                                    // 优先使用接口类型作为key，若无接口则使用实现类
                                    if (aClass.getInterfaces().length > 0) {
                                        beanFactory.put(aClass.getInterfaces()[0], instance);
                                    } else {
                                        beanFactory.put(aClass, instance);
                                    }
                                }
                            }
                        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

    /**
     * 执行依赖注入，处理所有Bean中带有@Di注解的字段
     */
    private void loadDi() {
        // 遍历所有已加载的Bean实例
        for (Map.Entry<Class<?>, Object> entry : beanFactory.entrySet()) {
            Object obj = entry.getValue();
            Class<?> objClass = obj.getClass();
            // 获取所有需要依赖注入的字段
            Field[] fields = objClass.getDeclaredFields();
            for (Field field : fields) {
                // 检查字段是否带有@Di注解
                Di diAnnotation = field.getAnnotation(Di.class);
                if (diAnnotation != null) {
                    try {
                        // 设置字段可访问并从容器中获取对应类型的Bean进行注入
                        field.setAccessible(true);
                        field.set(obj, beanFactory.get(field.getType()));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

}
